import numpy as np
import pandas as pd

import ice
import routs
import ship
import states
import fleet


def read_data(filepath_graph: str, filepath_ice: str, filepath_ships: str) ->\
        tuple[routs.Graph, np.datetime64, states.FleetState, fleet.Simulator]:
    """
    Read all data related to the problem.

    Args:
        filepath_graph: Path to the graph description.
        filepath_ice: Path to the ice data.
        filepath_ships: Path to the list of ship.

    Returns:
        The graph representing all possible routs, the initial time, the initial fleet state and
        the fleet simulator.
    """

    # Read the ice data.
    data_lat = pd.read_excel(filepath_ice, sheet_name="lat", header=None)
    data_lon = pd.read_excel(filepath_ice, sheet_name="lon", header=None)
    data_speed = pd.read_excel(filepath_ice, sheet_name="03-Mar-2022", header=None)

    # Create an ice cover model.
    ice_cover = ice.IceCover(data_lat.to_numpy(), data_lon.to_numpy(), data_speed.to_numpy())

    # Create a graph.
    graph = routs.Graph(filepath_graph, ice_cover)

    # Read the ship data.
    data_transports = pd.read_excel(filepath_ships, usecols="A:F", nrows=43)
    data_icebreakers = pd.read_excel(filepath_ships, usecols="C:F", skiprows=45, nrows=5)

    ship_speeds = dict()
    requests = list()
    transport_states = dict()
    icebreaker_states = dict()

    # Iterate over the transport ships.
    for i, row in data_transports.iterrows():
        # Parse the row.
        name = row["Название судна"]
        tmp = row["Ледовый класс"]
        tmp = tmp.lower()
        match tmp:
            case "arc 1":
                ice_class = 1
            case "arc 2":
                ice_class = 2
            case "arc 3":
                ice_class = 3
            case "arc 4":
                ice_class = 4
            case "arc 5":
                ice_class = 5
            case "arc 6":
                ice_class = 6
            case "arc 7":
                ice_class = 7
            case "arc 9":
                ice_class = 9
            case "нет":
                ice_class = 0
            case _:
                raise RuntimeError("invalid ice class")
        tmp = row["Скорость, узлы\n(по чистой воде)"]
        speed_no_ice = float(tmp)
        tmp = row["Пункт начала плавания"]
        tmp = tmp.lower()
        for node in graph.nodes:
            if node.name.lower() == tmp.lower():
                node_start = node
                break
        else:
            raise RuntimeError("invalid node")
        tmp = row["Пункт окончания плавания"]
        tmp = tmp.lower()
        for node in graph.nodes:
            if node.name.lower() == tmp:
                node_end = node
                break
        else:
            raise RuntimeError("invalid node")
        tmp = row["Дата начала плавания"]
        time = np.datetime64(f"{tmp.year}-{tmp.month:02}-{tmp.day:02}", "h")

        # Create a speed model.
        ship_speeds[name] = ship.ShipSpeed(ice_cover, ice_class, False, speed_no_ice)

        # Create a request for routing.
        requests.append(fleet.RequestForRouting(name, node_start, node_end, time))

        # Create a transport ship state.
        transport_states[name] = states.TransportShipState(node_start)

    # Iterate over the icebreakers.
    for i, row in data_icebreakers.iterrows():
        # Parse the row.
        name = row["Наименование"]
        ice_class = 9
        if name in {"50 лет Победы", "Ямал"}:
            mw60 = True
        else:
            mw60 = False
        tmp = row["Скорость, узлы \n(по чистой воде)"]
        speed_no_ice = float(tmp)
        tmp = row["Начальное положение ледоколов на 27 февраля 2022"]
        tmp = tmp.lower()
        for node in graph.nodes:
            if node.name.lower() == tmp:
                break
        else:
            raise RuntimeError("invalid node")

        # Create a speed model.
        ship_speeds[name] = ship.ShipSpeed(ice_cover, ice_class, mw60, speed_no_ice)

        # Create an icebreaker state.
        icebreaker_states[name] = states.IcebreakerState(node)

    # Set the initial time.
    time = np.datetime64("2022-02-27", "h")

    # Create a fleet state.
    state = states.FleetState(icebreaker_states, transport_states)

    # Create a fleet simulator.
    simulator = fleet.Simulator(requests, ship_speeds)

    return graph, time, state, simulator
